﻿using System;
using System.Collections.Generic;
using DependencyAnalysis;
using DependencyAnalysis.DataModel;
using DependencyAnalysis.Internal;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace UnitTests
{
    [TestClass]
    public class MetricsNumberOfReferencesTest
    {
        private Metrics _metrics;
        private DependencyModel _dependencyModel;

        [TestInitialize]
        public void Initialize()
        {
            _dependencyModel = new DependencyModel();
            var descriptor = DependencyModelTest.BuildDependencyDescriptor(1, 2, true, true);
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(1, 3, true, true);
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(2, 3, true, true);
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(3, 4, true, true);
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(4, 5, true, true);
            var type5 = descriptor.Target.ImplementingType;
            var module5 = descriptor.Target.ImplementingType.Submodule.Module;
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(4, 6, true, true);
            descriptor.Target.ImplementingType.Submodule.Module = module5;
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(4, 7, true, true);
            descriptor.Target.ImplementingType.Submodule.Module = module5;
            _dependencyModel.Add(descriptor);
            descriptor = DependencyModelTest.BuildDependencyDescriptor(4, 8, true, true);
            descriptor.Target.ImplementingType.Submodule.Module = module5;
            var type8 = descriptor.Target.ImplementingType;
            type8.InheritedTypes = new List<TypeDescriptor> { type5 };
            type5.ImplementingTypes = new List<TypeDescriptor> { type8 };
            _dependencyModel.Add(descriptor);
            _metrics = new MetricsImplementation(_dependencyModel);
        }

        [TestMethod]
        public void WhenBothModulesThenCountEachMember()
        {
            var source = new ModuleDescriptor { Identifier = "Module4" };
            var target = new ModuleDescriptor { Identifier = "Module5" };
            var numberOfReferences = _metrics.NumberOfReferences(source, target);
            Assert.AreEqual(4, numberOfReferences);
        }

        [TestMethod]
        public void WhenBothModulesAndEqualThenReturnZero()
        {
            var source = new ModuleDescriptor { Identifier = "Module4" };
            var target = new ModuleDescriptor { Identifier = "Module4" };
            var numberOfReferences = _metrics.NumberOfReferences(source, target);
            Assert.AreEqual(0, numberOfReferences);
        }

        [TestMethod]
        public void WhenBothSubmodulesAndEqualThenReturnZero()
        {
            var source = new SubmoduleDescriptor { Identifier = "Module4.Submodule4" };
            var target = new SubmoduleDescriptor { Identifier = "Module4.Submodule4" };
            var numberOfReferences = _metrics.NumberOfReferences(source, target);
            Assert.AreEqual(0, numberOfReferences);
        }

        [TestMethod]
        public void WhenBothTypesAndEqualThenReturnZero()
        {
            var source = new TypeDescriptor { Identifier = "Module4.Submodule4.Type4" };
            var target = new TypeDescriptor { Identifier = "Module4.Submodule4.Type4" };
            var numberOfReferences = _metrics.NumberOfReferences(source, target);
            Assert.AreEqual(0, numberOfReferences);
        }

        [TestMethod]
        public void WhenBothMemberssAndEqualThenReturnZero()
        {
            var source = new MemberDescriptor { Identifier = "Module4.Submodule4.Type4.Member4" };
            var target = new MemberDescriptor { Identifier = "Module4.Submodule4.Type4.Member4" };
            var numberOfReferences = _metrics.NumberOfReferences(source, target);
            Assert.AreEqual(0, numberOfReferences);
        }

        [TestMethod]
        public void WhenSourceUnsupportedTypeThenThrowArgumentException()
        {
            var source = new FakeDescriptor { Identifier = "Module4.Submodule4.Type4.Member4" };
            var target = new MemberDescriptor { Identifier = "Module4.Submodule4.Type4.Member4" };
            Assert1.Throws<ArgumentException>(() => _metrics.NumberOfReferences(source, target));
        }

        [TestMethod]
        public void WhenTargetUnsupportedTypeThenThrowArgumentException()
        {
            var source = new MemberDescriptor { Identifier = "Module4.Submodule4.Type4.Member4" };
            var target = new FakeDescriptor { Identifier = "Module4.Submodule4.Type4.Member4" };
            Assert1.Throws<ArgumentException>(() => _metrics.NumberOfReferences(source, target));
        }

        [TestMethod]
        public void WhenSourceModuleNotFoundThenReturnZero()
        {
            var source = new ModuleDescriptor { Identifier = "Module4a" };
            var target = new ModuleDescriptor { Identifier = "Module5" };
            var numberOfReferences = _metrics.NumberOfReferences(source, target);
            Assert.AreEqual(0, numberOfReferences);
        }

        [TestMethod]
        public void WhenSourceSubmoduleNotFoundThenReturnZero()
        {
            var source = new SubmoduleDescriptor { Identifier = "Module4a.Submodule4" };
            var target = new SubmoduleDescriptor { Identifier = "Module5.Submodule5" };
            var numberOfReferences = _metrics.NumberOfReferences(source, target);
            Assert.AreEqual(0, numberOfReferences);
        }

        [TestMethod]
        public void WhenSourceTypeNotFoundThenReturnZero()
        {
            var source = new TypeDescriptor { Identifier = "Module4a.Submodule4.Type4" };
            var target = new TypeDescriptor { Identifier = "Module5.Submodule5.Type5" };
            var numberOfReferences = _metrics.NumberOfReferences(source, target);
            Assert.AreEqual(0, numberOfReferences);
        }

        [TestMethod]
        public void WhenSourceMemberNotFoundThenReturnZero()
        {
            var source = new MemberDescriptor { Identifier = "Module4a.Submodule4.Type4.Member4" };
            var target = new MemberDescriptor { Identifier = "Module5.Submodule5.Type5.Member5" };
            var numberOfReferences = _metrics.NumberOfReferences(source, target);
            Assert.AreEqual(0, numberOfReferences);
        }

        [TestMethod]
        public void WhenTargetModuleNotFoundThenReturnZero()
        {
            var source = new ModuleDescriptor { Identifier = "Module4" };
            var target = new ModuleDescriptor { Identifier = "Module5a" };
            var numberOfReferences = _metrics.NumberOfReferences(source, target);
            Assert.AreEqual(0, numberOfReferences);
        }

        [TestMethod]
        public void WhenTargetSubmoduleNotFoundThenReturnZero()
        {
            var source = new SubmoduleDescriptor { Identifier = "Module4.Submodule4" };
            var target = new SubmoduleDescriptor { Identifier = "Module5a.Submodule5" };
            var numberOfReferences = _metrics.NumberOfReferences(source, target);
            Assert.AreEqual(0, numberOfReferences);
        }

        [TestMethod]
        public void WhenTargetTypeNotFoundThenReturnZero()
        {
            var source = new TypeDescriptor { Identifier = "Module4.Submodule4.Type4" };
            var target = new TypeDescriptor { Identifier = "Module5a.Submodule5.Type5" };
            var numberOfReferences = _metrics.NumberOfReferences(source, target);
            Assert.AreEqual(0, numberOfReferences);
        }

        [TestMethod]
        public void WhenTargetMemberNotFoundThenReturnZero()
        {
            var source = new MemberDescriptor { Identifier = "Module4.Submodule4.Type4.Member4" };
            var target = new MemberDescriptor { Identifier = "Module5a.Submodule5.Type5.Member5" };
            var numberOfReferences = _metrics.NumberOfReferences(source, target);
            Assert.AreEqual(0, numberOfReferences);
        }

        [TestMethod]
        public void WhenTargetTypeHasVariantThenAggregateCount()
        {
            var source = new TypeDescriptor { Identifier = "Module4.Submodule4.Type4" };
            var target = new TypeDescriptor { Identifier = "Module5.Submodule5.Type5" };
            var numberOfReferences = _metrics.NumberOfReferences(source, target);
            Assert.AreEqual(2, numberOfReferences);
        }

        private class FakeDescriptor : IDescriptor
        {
            public string Identifier { get; set; }

            public string FullName
            {
                get { return Identifier; }
            }

            public IDescriptor Parent
            {
                get { return null; }
            }
        }
    }
}
